#include "./fiip.h"
#include "riselib/linkedList.h"

uint8_t fiip_centerId[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t fiip_broadId[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
FormatStruct* fiip_rxd;
uint8_t fiip_relay;

typedef struct {
  LinkCfgStruct* link;
  FormatStruct* rxd;
} RemanentBufStruct;                //残余数据
LinkedListStruct* remanentBufList;  //残余数据链表
static ProtocolStruct* protocols[16];
static uint16_t protocolsLen;
static uint8_t myId[8];

void fiip_init(uint8_t relay) {
  fiip_relay = relay;
  if (remanentBufList == NULL)
    remanentBufList = linkedList_new();
  if (fiip_rxd == NULL)
    fiip_rxd = format_new();
  if (fiip_getProtocol(0) == NULL) {
    ProtocolStruct protocol;
    protocol.type = 0;
    protocol.solve = fiipListener_solve;
    fiip_addProtocol(0, &protocol);
  }
}
void fiip_setId(uint8_t* id) {
  memcpy(myId, id, 8);
}

void fiip_addProtocol(uint8_t type, ProtocolStruct* protocol) {
  ProtocolStruct* myProtocol = NULL;
  for (int i = 0; i < protocolsLen; i++) {
    if (protocols[i] != NULL) {
      if (protocols[i]->type == type) {
        INFO("fiip protocol %02X has been replaced.\n", type);
        mem_cpy(protocols[i], protocol, sizeof(ProtocolStruct));
        protocols[i]->type = type;
        return;
      }
    }
  }

  if (protocolsLen < 16) {
    myProtocol = (ProtocolStruct*)mempool_alloc(sizeof(ProtocolStruct));
    mem_cpy(myProtocol, protocol, sizeof(ProtocolStruct));
    myProtocol->type = type;
    protocols[protocolsLen] = myProtocol;
    protocolsLen++;
  } else {
    WARN("fiip has too much protocol.\n");
  }
}
ProtocolStruct* fiip_getProtocol(uint8_t type) {
  for (int i = 0; i < protocolsLen; i++) {
    if (protocols[i] != NULL) {
      if (protocols[i]->type == type) {
        return protocols[i];
      }
    }
  }
  return NULL;
}

void fiip_sendOriginal(uint8_t* data, uint16_t dataLen, LinkCfgStruct* link) {
#ifdef DEBUG
  printf("send to link:%02X;%d.%d.%d.%d:%d:\n", link->type, link->address[0],
         link->address[1], link->address[2], link->address[3], link->port);
  for (uint8_t i = 0; i < dataLen; i++) {
    printf("%02X", data[i]);
  }
  printf("\n");
#endif

  link->send(data, dataLen, link);
}
void fiip_broadcast(FormatStruct* frame) {
  frame->buf[format_field_ttl] = 0x08;
  frame->buf[format_field_control] = 0x00;
  format_setVar(frame, format_field_srcAddr, myId, 8);
  format_getData(frame);
  for (uint16_t i = 0; i < route_len; i++) {
    fiip_sendOriginal(frame->buf, *frame->bufLen, route_links[i]->link);
  }
}
void fiip_request(FormatStruct* frame, uint8_t* id, LinkCfgStruct* link) {
  if (id == NULL) {
    id = frame->msg->dstAddr;
  } else {
    format_setVar(frame, format_field_dstAddr, id, 8);
  }
  if (link == NULL) {
    link = route_find(id)->link;
  }
  if (link) {
    frame->buf[format_field_ttl] = 0x08;
    frame->buf[format_field_control] = 0x00;
    format_setVar(frame, format_field_srcAddr, myId, 8);
    format_getData(frame);
    fiipRetransmit_add(frame->buf, *frame->bufLen, link);
    fiip_sendOriginal(frame->buf, *frame->bufLen, link);
  } else {
    WARN("fiip no route,can't request.\n\n");
  }
}
void fiip_respond(FormatStruct* frame,
                  Format_FieldStruct* msg,
                  uint8_t error,
                  uint8_t* body,
                  uint8_t bodyLen) {
  LinkCfgStruct* link = route_find(msg->srcAddr)->link;
  if (!link) {
    WARN("fiip no route,can't respond.\n\n");
    return;
  }

  uint8_t fiip_cmd[2] = {(uint8_t)(msg->cmd[0] | 0x80), msg->cmd[1]};
  frame->buf[format_field_ttl] = 0x08;
  frame->buf[format_field_control] = 0x01;
  format_setVar(frame, format_field_srcAddr, myId, 8);
  format_setVar(frame, format_field_dstAddr, msg->srcAddr, 8);
  format_setVar(frame, format_field_cmd, fiip_cmd, 2);
  format_setVar(frame, format_field_body, body, bodyLen);
  format_setVar(frame, format_field_index, msg->index, 2);
  frame->buf[format_field_errno] = error;
  format_getData(frame);
  fiip_sendOriginal(frame->buf, *frame->bufLen, link);
}

void fiip_recv(uint8_t* buf, uint16_t bufLen, LinkCfgStruct* link) {
  int8_t recvStatus = -1;
  RemanentBufStruct* remanentBuf;
  LinkedListUnitStruct* current;

  current = remanentBufList->first;
  while (current != NULL) {
    remanentBuf = (RemanentBufStruct*)current->data;
    if (remanentBuf->link->type == link->type) {
      if (remanentBuf->link->port == link->port) {
        if (memcmp(remanentBuf->link->address, link->address, 8) == 0) {
          recvStatus = bufRecver_recv(remanentBuf->rxd->bufRecver, buf, bufLen);
          if (recvStatus == 0) {
            linkedList_delData(remanentBufList, NULL);
            format_del(fiip_rxd);
            fiip_rxd = remanentBuf->rxd;
            link_del(remanentBuf->link);
            mempool_free(remanentBuf);
            break;
          } else {
            return;
          }
        }
      }
    }
    current = current->next;
  }

  if (recvStatus == -1) {
    recvStatus = bufRecver_recv(fiip_rxd->bufRecver, buf, bufLen);
  }
  if (recvStatus == 0) {
#ifdef DEBUG
    printf("recv from link:%02X;%d.%d.%d.%d:%d:\n", link->type,
           link->address[0], link->address[1], link->address[2],
           link->address[3], link->port);
    for (uint8_t i = 0; i < bufLen; i++) {
      printf("%02X", buf[i]);
    }
    printf("\n");
#endif
    if (format_getMsg(fiip_rxd)) {
      route_update(fiip_rxd->msg->srcAddr, link);  //更新路由

      if (memcmp(fiip_rxd->msg->dstAddr, myId, 8) == 0) {  //执行
        if (fiip_rxd->msg->control[0] == 0x01) {
          fiipRetransmit_remove((fiip_rxd->msg->index[0] << 8) +
                                fiip_rxd->msg->index[1]);
        }

        for (uint8_t i = 0; i < protocolsLen; i++) {
          if (protocols[i]->type == fiip_rxd->msg->type[0]) {
            protocols[i]->solve(fiip_rxd->msg, link);
            return;
          }
        }
        WARN("fiip no protocol type:%02X.\n", fiip_rxd->msg->type[0]);
      } else if (fiip_relay) {  //转发
        RouteStruct* route = route_find(fiip_rxd->msg->dstAddr);
        LinkCfgStruct* link = route->link;
        if (link) {
          if (fiip_rxd->msg->ttl[0] > 0) {
            *fiip_rxd->bufLen = *fiip_rxd->bufLen - 7;
            fiip_rxd->buf[format_field_ttl] -= 1;
            format_getData(fiip_rxd);
            fiip_sendOriginal(fiip_rxd->buf, *fiip_rxd->bufLen, link);
          } else {
            WARN("fiip TTL is %d.\n", fiip_rxd->msg->ttl[0]);
          }
        } else {
          WARN("fiip no route,can't forward.\n");
        }
      }
    } else {
      WARN("fiip crc error\n");
    }
  } else {
    if (recvStatus == 1) {
      RemanentBufStruct* remanentBuf =
          (RemanentBufStruct*)mempool_alloc(sizeof(RemanentBufStruct));
      remanentBuf->link = link_new(link);
      remanentBuf->rxd = fiip_rxd;
      fiip_rxd = format_new();
      linkedList_addData(remanentBufList, remanentBuf);
    } else {
      WARN("bufRecver status: %d.\n", recvStatus);
    }
  }
}
void fiip_connect(uint8_t* id, LinkCfgStruct* link) {
  route_remove(id);
  route_update(id, link);
  route_fix(id);
}
